package com.sky.state.CM;


import java.util.Random;

/**
 * @author : wushikai
 * <p>
 * date : 2022-03-28
 * 流程1=> 转动曲柄给、机器发放糖果
 * 流程2=> 投入硬币、退回硬币
 */

interface State {

    void insertCoin();

    void returnCoin();

    void turnCrank();

    void dispense();

    void printState();
}


class HasCoin implements State {
    private CandyMachine machine;

    public HasCoin(CandyMachine machine) {
        this.machine = machine;
    }

    @Override
    public void insertCoin() {
        System.out.println("you can't insert another coin!");

    }

    @Override
    public void returnCoin() {
        System.out.println("coin return!");
        machine.setCurrentState(machine.onReadyState);
    }

    @Override
    public void turnCrank() {
        System.out.println("crank turn...!");
        Random r = new Random();
        int i = r.nextInt(10);
        if (i == 0) {
            machine.setCurrentState(machine.winnerState);   //额外获奖, 暴击

        } else {
            machine.setCurrentState(machine.soldState);    //普通售出

        }

    }

    @Override
    public void dispense() {
    }

    @Override
    public void printState() {
        System.out.println("***HasCoin***");

    }

}


class WinnerState implements State {

    private CandyMachine machine;

    public WinnerState(CandyMachine machine) {
        this.machine = machine;
    }

    @Override
    public void insertCoin() {
        System.out.println("please wait!we are giving you a candy!");

    }

    @Override
    public void returnCoin() {
        System.out.println("you haven't inserted a coin yet!");

    }

    @Override
    public void turnCrank() {
        System.out
                .println("we are giving you a candy,turning another get nothing,!");

    }

    @Override
    public void dispense() {


        machine.releaseCandy();
        if (machine.getCandyCount() == 0) {
            machine.setCurrentState(machine.soldOutState);
        } else {
            System.out.println("you are a winner!  you get another candy!");
            machine.releaseCandy();
            if (machine.getCandyCount() > 0) {
                machine.setCurrentState(machine.onReadyState);
            } else {
                System.out.println("oh  shit ,   out of candies");
                machine.setCurrentState(machine.soldOutState);
            }
        }

    }

    @Override
    public void printState() {
        System.out.println("***WinnerState***");

    }

}


class SoldState implements State {
    private CandyMachine machine;

    public SoldState(CandyMachine mCandyMachine) {
        this.machine = mCandyMachine;
    }

    @Override
    public void insertCoin() {

        System.out.println("please wait!we are giving you a candy!");

    }

    @Override
    public void returnCoin() {

        System.out.println("you haven't inserted a coin yet!");

    }

    @Override
    public void turnCrank() {

        System.out.println("we are giving you a candy,turning another get nothing,!");

    }

    @Override
    public void dispense() {


        machine.releaseCandy();
        if (machine.getCandyCount() > 0) {
            machine.setCurrentState(machine.onReadyState);
        } else {
            System.out.println("oh shit,  out of candies");
            machine.setCurrentState(machine.soldOutState);
        }


    }

    @Override
    public void printState() {

        System.out.println("***SoldState***");

    }

}

class OnReadyState implements State {
    private CandyMachine machine;

    public OnReadyState(CandyMachine machine) {
        this.machine = machine;
    }

    @Override
    public void insertCoin() {

        System.out.println("you have inserted a coin,  next,  please turn crank!");
        machine.setCurrentState(machine.hasCoin);
    }

    @Override
    public void returnCoin() {

        System.out.println("you haven't inserted a coin yet!");

    }

    @Override
    public void turnCrank() {

        System.out.println("you turned,  but you haven't inserted a coin!");

    }

    @Override
    public void dispense() {


    }

    @Override
    public void printState() {

        System.out.println("***OnReadyState***");

    }

}


class SoldOutState implements State {

    private CandyMachine machine;

    public SoldOutState(CandyMachine machine) {
        this.machine = machine;
    }

    @Override
    public void insertCoin() {

        System.out.println("you can't insert coin,the machine sold out!");

    }

    @Override
    public void returnCoin() {

        System.out.println("you can't return,you haven't inserted a coin yet!");

    }

    @Override
    public void turnCrank() {

        System.out.println("you turned,but there are no candies!");

    }

    @Override
    public void dispense() {


    }

    @Override
    public void printState() {

        System.out.println("***SoldOutState***");

    }

}

public class CandyMachine {

    State soldOutState;
    State onReadyState;
    State hasCoin;
    State soldState;
    State winnerState;
    private State currentState;
    private int candyCount = 0;

    public CandyMachine(int candyCount) {
        this.candyCount = candyCount;
        soldOutState = new SoldOutState(this);
        onReadyState = new OnReadyState(this);
        hasCoin = new HasCoin(this);
        soldState = new SoldState(this);
        winnerState = new WinnerState(this);
        if (candyCount > 0) {
            currentState = onReadyState;
        } else {
            currentState = soldOutState;
        }
    }

    public void setCurrentState(State currentState) {
        this.currentState = currentState;
    }

    public void insertCoin() {
        currentState.insertCoin();
    }

    public void returnCoin() {
        currentState.returnCoin();
    }

    public void turnCrank() {
        currentState.turnCrank();
        currentState.dispense();
    }


    /**
     * 发放糖果
     * */
    void releaseCandy() {

        if (candyCount > 0) {
            candyCount = candyCount - 1;
            System.out.println("a candy rolling out!");
        }

    }

    public int getCandyCount() {
        return candyCount;
    }

    public void printState() {
        currentState.printState();
    }
}


class Client {


    /**
     *
     * 明明是同样的代码,  怎么走着走着就变化了? 即当糖果的数量为0 的时候, 状态也变化了, 处理也变化了;
     * 实际上是使用组合通过简单引用不同的状态对象来造成类改变的假象
     *
     * 状态模式要点
     *
     * (1)客户不会和状态进行交互,全盘了解状态是 context(  CandyMachine )的工作
     * (2)在状态模式中，每个状态通过持有Context ( CandyMachine ) 的引用，来实现状态转移
     * (3)使用状态模式总是会增加设计中类的数目，这是为了要获得程序可扩展性，弹性的代价，如果你的代码不是一次性的，后期可能会不断加入不同的状态，那么状态模式的设计是绝对值得的。
     *
     *
     *
     *  一句话：最根本的差异在于策略模式是在求解同一个问题的多种解法，这些不同解法之间毫无关联；状态模式则不同，状态模式要求各个状态之间有所关联，以便实现状态转移。
     *
     * */
    public static void main(String[] args) {
        CandyMachine machine = new CandyMachine(6);

        machine.printState();

        machine.insertCoin();
        machine.printState();

        machine.turnCrank();

        machine.printState();

        machine.insertCoin();
        machine.printState();

        machine.turnCrank();

        machine.printState();
    }
}
